/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package Model;


import Control.Triagem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;

/**
 *
 * @author Alan Carlos
 * @author João Vilas Boas
 */
/*
Classe que representa a tabela de simbolos do compilador.
Possui metodos de inserção e remoção de tokens na estrutura de dados
desejada (pode ser uma ArrayList ou uma Tabela Hash) e inserção de 
tokens mal formados em uma ArrayList de erros.

*/

public class TableLexema {

    private static TableLexema instance = null;
    private int row;
    private int column;
    private String lexema;
    private String token;
    //Deixar a implementação da tabela Hash, para usar quando necessário
    private Hashtable<String,Token> table;
    private  ArrayList<Token> array;
    private  ArrayList<ErrorLexema> errorTable;
    private ArrayList<ErrorSintatico> errosSintaticos;
    private ArrayList<ErrorSemantico> errosSemanticos;
    
    
    private Collection<IndexTexto> indexTextoCollection;
    
    private TableLexema() {        
        /*A key da tabela Hash será o lexema e 
        a tabela sera composta de objetos do tipo Token*/
        this.table = new Hashtable<String,Token>();
        this.array = new ArrayList<Token>();
        this.errorTable = new ArrayList<ErrorLexema>();
        this.errosSintaticos = new ArrayList<ErrorSintatico>();
        this.errosSemanticos = new ArrayList<>();
       
        
        
    }

    public String getLexema() {
        return lexema;
    }

    public void setLexema(String lexema) {
        this.lexema = lexema;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public ArrayList<ErrorSintatico> getErrosSintaticos() {
        return errosSintaticos;
    }

    public void setErrosSintaticos(ArrayList<ErrorSintatico> errosSintaticos) {
        this.errosSintaticos = errosSintaticos;
    }

    public ArrayList<ErrorSemantico> getErrosSemanticos() {
        return errosSemanticos;
    }

    public Collection<IndexTexto> getIndexTextoCollection() {
        return indexTextoCollection;
    }

    public void setIndexTextoCollection(Collection<IndexTexto> indexTextoCollection) {
        this.indexTextoCollection = indexTextoCollection;
    }
    public static synchronized TableLexema getInstance() {
		if (instance == null) {
                	instance = new TableLexema ();
		}
		return instance;
	}

    public int getRow() {
        return row;
    }

    public Hashtable<String, Token> getTable() {
        return table;
    }

    public void setTable(Hashtable<String, Token> table) {
        this.table = table;
    }

    public ArrayList<ErrorLexema> getErrorTable() {
        return errorTable;
    }

    public void setErrorTable(ArrayList<ErrorLexema> errorTable) {
        this.errorTable = errorTable;
    }

    public ArrayList<Token> getArray() {
        return array;
    }

    public void setArray(ArrayList<Token> array) {
        this.array = array;
    }

    public int getColumn() {
        return column;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public void setColumn(int column) {
        this.column = column;
    }

    public void incrementRow(){
        this.column=1;
        this.row++;        
    }
    public void incrementColumn(){
        this.column++;
    }
    
    public void setIndex(String texto) {
         
        String[] linhas = texto.split("\\n");
        
        
        /*cria um vetor de strings onde cada posição deste vetor 
        é composta por uma linha do codigo fonte*/
        
        //criar novas estruturas de dados cada vez que esse metodo for chamado
        //necessário para limpar as tabelas a cada vez que o botão compilar for acionado
        this.indexTextoCollection = new ArrayList<IndexTexto>(texto.length());
        this.table = new Hashtable<String,Token>();
        this.array = new ArrayList<Token>();
        this.errorTable = new ArrayList<ErrorLexema>();
        this.errosSintaticos = new ArrayList<ErrorSintatico>();
        this.errosSemanticos = new ArrayList<>();
        //arrayIndex = new int[linhas.length][40];    
       
        int index = 0;
        for (int row = 0; row < linhas.length; row++) {
            // a cada incremento na leitura das linhas, apontar para 
            //o primeiro caractere
          
            for (int col = 0; col < linhas[row].length(); col++) { 
                //percorre todos os caracteres até o final da linha
                //arrayIndex[row][col]=index;
                //char caractere=  linhas[row].charAt(col);
                /* associa a cada indice de um determinado caractere no codigo fonte, 
                um valor de linha e coluna*/
                indexTextoCollection.add(new IndexTexto(row+1, col+1, index)); 
                
                index++;
            }
           // if(linhas[row].charAt(linhas[row].length()-1)!=Triagem.EOF){
            indexTextoCollection.add(new IndexTexto(row+1,linhas[row].length()+1, index));           
            index++;
           // }
        }

    }  
    
    public IndexTexto getIndexTexto(int index){
        //retorna um objeto do tipo Index, contendo um indice do caractere, 
        //a posição na linha e na coluna em que se encontra
       for (IndexTexto aux  : indexTextoCollection){
           if(aux.getIndice()==index){
               return aux;
           }
       }
     return null;    
   }
    
    public void addToList(String lexema, int indice, int tipo){
        //metodo para adicionar um token no ArrayList de Tokens
        IndexTexto aux = TableLexema.getInstance().getIndexTexto(indice);
        //adiciono informações da posição que esse token vai estar ao ser adicionado no array
        //dessa forma, podemos fazer buscas por tokens especificos a partir da sua localização no array
        int aux2 = TableLexema.getInstance().getArray().size();
        Token reserved = new Token(lexema, aux.getLinha(), aux.getColuna(), tipo, aux2);
        TableLexema.getInstance().getArray().add(reserved);
        
    }
    
    public void addErrorToList(String lexema, int indice, String tipoErro){
        //metodo para adicionar objetos que representam os 
        //erros lexicos na ArrayList de Erros léxicos
        IndexTexto aux = TableLexema.getInstance().getIndexTexto(indice);
        ErrorLexema reserved = new ErrorLexema(lexema, aux.getLinha(), aux.getColuna(), tipoErro);
        TableLexema.getInstance().getErrorTable().add(reserved);
        
    }
    public void addErroSintatico(String sintatico, int linha, int coluna,  String msgErro){
        //metodo para adicionar objetos que representam os 
        //erros sintaticoos na ArrayList de Erros Sintaticos
        ErrorSintatico aux = new ErrorSintatico(sintatico, linha, coluna, msgErro);
        TableLexema.getInstance().getErrosSintaticos().add(aux);
        
    }
    public void addErroSemantico(String semantico, int linha, int coluna,  String msgErro){
        //metodo para adicionar objetos que representam os 
        //erros sintaticoos na ArrayList de Erros Sintaticos
        ErrorSemantico erroSemantico= new ErrorSemantico(semantico, linha, coluna, msgErro);
        errosSemanticos.add(erroSemantico);
        System.out.println(erroSemantico.getTipoErro());
        
        
    }  
    
    
}

/* IMPLEMENTAÇÃO DE CODIGO PARA INSERÇÃO NA TABELA HASH
 UTILIZAR QUANDO FOR NECESSÁRIO

 IndexTexto aux = TableLexema.getInstance().getIndexTexto(i-1);
 Token found =  (Token) TableLexema.getInstance().getTable().get(lexemaI);
                    
 if(found!=null){
 //Se eu ja tiver um mesmo token dentro da tabela HASH
 //eu so faço adicionar o indice em que esse outro token se encontra
 found.getListIndex().add(aux);
 }

 else{//caso contrario, crio um novo objeto Token e insiro na tabela HASH

 LinkedList<IndexTexto> indices = new LinkedList<IndexTexto>();
 indices.add(aux);
 Token reserved = new Token(lexemaI, indices, 2);
 TableLexema.getInstance().getTable().put(lexemaI, reserved);
 }


 */
